home *** CD-ROM | disk | FTP | other *** search
-
- /*
- * C P P 4 . C
- * M a c r o D e f i n i t i o n s
- *
- * Edit History
- * 31-Aug-84 MM USENET net.sources release
- * 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring()
- * so they work correctly with token concatenation.
- * Added string formal recognition.
- * 25-Oct-84 MM "Short-circuit" evaluate #if's so that we
- * don't print unnecessary error messages for
- * #if !defined(FOO) && FOO != 0 && 10 / FOO ...
- * 31-Oct-84 ado/MM Added token concatenation
- * 6-Nov-84 MM Split off eval stuff
- * 1-Apr-85 ado Fixed bug in STRING_FORMAL version
- * 2-May-85 MM Changed the way macro parameters work -- only
- * one byte is reserved, 255 param's possible.
- */
-
- #include <stdio.h>
- #include <ctype.h>
- #include "cppdef.h"
- #include "cpp.h"
- #if OK_CONCAT == CON_NOEXPAND
- #define SEP COM_SEP /* concat token, don't reexpand */
- #endif
- #if OK_CONCAT == CON_EXPAND
- #define SEP TOK_SEP /* concat token and reexpand */
- #endif
-
- /*
- * parm[], parmp, and parlist[] are used to store #define() argument
- * lists. nargs contains the actual number of parameters stored.
- */
- static char parm[NPARMWORK + 1]; /* define param work buffer */
- static char *parmp; /* Free space in parm */
- static char *parlist[NMACPARS]; /* -> start of each parameter */
- static int nargs; /* Parameters for this macro */
-
- dodefine()
- /*
- * Called from control when a #define is scanned. This module
- * parses formal parameters and the replacement string. When
- * the formal parameter name is encountered in the replacement
- * string, it is replaced by a character in the range 128 to
- * 128+NPARAM (this allows up to 32 parameters within the
- * Dec Multinational range). If cpp is ported to an EBCDIC
- * machine, you will have to make other arrangements.
- *
- * There is some special case code to distinguish
- * #define foo bar
- * from #define foo() bar
- *
- * Also, we make sure that
- * #define foo foo
- * expands to "foo" but doesn't put cpp into an infinite loop.
- *
- * A warning message is printed if you redefine a symbol to a
- * different text. I.e,
- * #define foo 123
- * #define foo 123
- * is ok, but
- * #define foo 123
- * #define foo +123
- * is not.
- *
- * The following subroutines are called from define():
- * checkparm called when a token is scanned. It checks through the
- * array of formal parameters. If a match is found, the
- * token is replaced by a control byte which will be used
- * to locate the parameter when the macro is expanded.
- * textput puts a string in the macro work area (parm[]), updating
- * parmp to point to the first free byte in parm[].
- * textput() tests for work buffer overflow.
- * charput puts a single character in the macro work area (parm[])
- * in a manner analogous to textput().
- */
- {
- register int c;
- register DEFBUF *dp; /* -> new definition */
- int isredefine; /* TRUE if redefined */
- char *old; /* Remember redefined */
- extern int save(); /* Save char in work[] */
-
- if (type[(c = skipws())] != LET)
- goto bad_define;
- isredefine = FALSE; /* Set if redefining */
- if ((dp = lookid(c)) == NULL) /* If not known now */
- dp = defendel(token, FALSE); /* Save the name */
- else { /* It's known: */
- isredefine = TRUE; /* Remember this fact */
- old = dp->repl; /* Remember replacement */
- dp->repl = NULL; /* No replacement now */
- }
- parlist[0] = parmp = parm; /* Setup parm buffer */
- if ((c = get()) == '(') { /* With arguments? */
- nargs = 0; /* Init formals counter */
- do { /* Collect formal parms */
- if (nargs >= NMACPARS)
- cfatal("Too many arguments for macro", NULLST);
- else if ((c = skipws()) == ')')
- break; /* Got them all */
- else if (type[c] != LET) /* Bad formal syntax */
- goto bad_define;
- scanid(c); /* Get the formal param */
- parlist[nargs++] = parmp; /* Save its start */
- textput(token); /* Save text in parm[] */
- } while ((c = skipws()) == ','); /* Get another argument */
- if (c != ')') /* Must end at ) */
- goto bad_define;
- c = ' '; /* Will skip to body */
- }
- else {
- /*
- * DEF_NOARGS is needed to distinguish between
- * "#define foo" and "#define foo()".
- */
- nargs = DEF_NOARGS; /* No () parameters */
- }
- if (type[c] == SPA) /* At whitespace? */
- c = skipws(); /* Not any more. */
- workp = work; /* Replacement put here */
- inmacro = TRUE; /* Keep \<newline> now */
- for (; c != EOF_CHAR && c != '\n'; c = get()) {
- #if OK_CONCAT != CON_FALSE
- if (c == '#') { /* String/concat? */
- if ((c = get()) == '#') { /* Concatenate tokens? */
- while (workp > work && type[workp[-1]] == SPA)
- --workp; /* Erase leading spaces */
- save(SEP);
- c = skipws(); /* Eat whitespace */
- switch (type[c]) { /* What flavor of token */
- case LET:
- checkparm(c, dp); /* Save it normally */
- break;
-
- case DIG:
- do { /* Stuff the digits */
- save(c);
- c = get();
- } while (type[c] == DIG);
- break;
-
- default:
- ciwarn("Strange character after # (%d.)", c);
- save(c);
- break;
- }
- save(SEP); /* Delimit 2nd token */
- }
- else { /* Stringize */
- unget(); /* Gotta rescan it */
- /*
- * We store a magic cookie (which becomes " on output)
- * so the macro expander doesn't block expansion
- * of the actual parameter. For example,
- * #define abc(a) #a
- * abc(__LINE__)
- * should yield "123", not "__LINE__".
- * This is a hack, and probably going to cause trouble.
- */
- save(ST_QUOTE);
- if ((c = isformal(skipws())) == 0) {
- cwarn("Expected formal parameter, got \"%s\"", token);
- mtokensave(dp);
- }
- save(ST_QUOTE);
- }
- continue; /* Done with this token */
- }
- #endif
- switch (type[c]) {
- case LET:
- checkparm(c, dp); /* Might be a formal */
- break;
-
- case DIG: /* Number in mac. body */
- case DOT: /* Maybe a float number */
- scannumber(c, save); /* Scan it off */
- break;
-
- case QUO: /* String in mac. body */
- #if STRING_FORMAL
- stparmscan(c, dp); /* Do string magic */
- #else
- stparmscan(c);
- #endif
- break;
-
- case BSH: /* Backslash */
- save('\\');
- if ((c = get()) == '\n')
- wrongline = TRUE;
- save(c);
- break;
-
- case SPA: /* Absorb whitespace */
- /*
- * Note: the "end of comment" marker is passed on
- * to allow comments to separate tokens.
- */
- if (workp[-1] == ' ') /* Absorb multiple */
- break; /* spaces */
- else if (c == '\t')
- c = ' '; /* Normalize tabs */
- /* Fall through to store character */
- default: /* Other character */
- save(c);
- break;
- }
- }
- inmacro = FALSE; /* Stop newline hack */
- unget(); /* For control check */
- if (workp > work && workp[-1] == ' ') /* Drop trailing blank */
- workp--;
- *workp = EOS; /* Terminate work */
- dp->repl = savestring(work); /* Save the string */
- dp->nargs = nargs; /* Save arg count */
- #if DEBUG
- if (debug)
- dumpadef("macro definition", dp);
- #endif
- if (isredefine) { /* Error if redefined */
- if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl))
- || (old == NULL && dp->repl != NULL)
- || (old != NULL && dp->repl == NULL)) {
- cerror("Redefining defined variable \"%s\"", dp->name);
- }
- if (old != NULL) /* We don't need the */
- free(old); /* old definition now. */
- }
- return;
-
- bad_define:
- cerror("#define syntax error", NULLST);
- inmacro = FALSE; /* Stop <newline> hack */
- }
-
-
-
-
-
-
-
-
- checkparm(c, dp)
- register int c;
- DEFBUF *dp;
- /*
- * Replace this param if it's defined. Note that the macro name is a
- * possible replacement token. We stuff DEF_MAGIC in front of the token
- * which is treated as a LETTER by the token scanner and eaten by
- * the output routine. This prevents the macro expander from
- * looping if someone writes "#define foo foo".
- */
- {
- if ((c = isformal(c)) == 0)
- mtokensave(dp);
- }
-
- FILE_LOCAL int
- isformal(c)
- register int c;
- /*
- * Scan the token starting with c. If it is a formal parameter, save
- * the MAC_PARM and formal offset, returning TRUE. Else, return FALSE.
- */
- {
- register int i;
-
- scanid(c); /* Get parm to token[] */
- for (i = 0; i < nargs; i++) { /* For each argument */
- if (streq(parlist[i], token)) { /* If it's known */
- save(MAC_PARM); /* Save the signal */
- save(i + 1); /* Save the formal */
- return (TRUE); /* Return "gotcha" */
- }
- }
- return (FALSE); /* Not a formal param */
- }
-
- FILE_LOCAL
- mtokensave(dp)
- DEFBUF *dp;
- /*
- * Save the token in the macro buffer. A magic cookie is saved
- * if the token is identical to the macro name, so the expansion
- * doesn't recurse.
- */
- {
- register char *cp;
-
- if (dp != NULL && streq(dp->name, token)) /* Macro name in body */
- save(DEF_MAGIC); /* Save magic marker */
- for (cp = token; *cp != EOS;) /* And save */
- save(*cp++); /* The token itself */
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #if STRING_FORMAL
- stparmscan(delim, dp)
- int delim;
- register DEFBUF *dp;
- /*
- * Scan the string (starting with the given delimiter).
- * The token is replaced if it is the only text in this string or
- * character constant. The algorithm follows checkparm() above.
- * Note that scanstring() has approved of the string.
- */
- {
- register int c;
-
- /*
- * Warning -- this code hasn't been tested for a while.
- * It exists only to preserve compatibility with earlier
- * implementations of cpp. It is not part of the Draft
- * ANSI Standard C language.
- */
- save(delim);
- instring = TRUE;
- while ((c = get()) != delim
- && c != '\n'
- && c != EOF_CHAR) {
- if (type[c] == LET) /* Maybe formal parm */
- checkparm(c, (DEFBUF *) NULL); /* But no DEF_MAGIC */
- else {
- save(c);
- if (c == '\\')
- save(get());
- }
- }
- instring = FALSE;
- if (c != delim)
- cerror("Unterminated string in macro body", NULLST);
- save(c);
- }
- #else
- stparmscan(delim)
- int delim;
- /*
- * Normal string parameter scan.
- */
- {
- register char *wp;
- register int i;
- extern int save();
-
- wp = workp; /* Here's where it starts */
- if (!scanstring(delim, save))
- return; /* Exit on scanstring error */
- #if 0 && STRING_FORMAL
- /*
- * This code -- if reenabled -- recognizes a formal parameter
- * if it is the only component of a string:
- * #define foo(bar, v) printf("%" "bar" "\n", v);
- * This has been superceded by # stringizing.
- */
- workp[-1] = EOS; /* Erase trailing quote */
- wp++; /* -> first string content byte */
- #if (NMACPARS * 2) + 1 > 255
- << error, the following won't work >>
- #endif
- for (i = 0; i < nargs; i++) {
- if (streq(parlist[i], wp)) {
- *wp++ = MAC_PARM; /* Parameter signal */
- *wp++ = i + NMACPARS + 1; /* Out of range marker */
- *wp = wp[-3]; /* Add on closing quote */
- workp = wp + 1; /* Reset string end */
- return;
- }
- }
- workp[-1] = wp[-1]; /* Nope, reset end quote. */
- #endif
- }
- #endif
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- doundef()
- /*
- * Remove the symbol from the defined list.
- * Called from the #control processor.
- */
- {
- register int c;
-
- if (type[(c = skipws())] != LET)
- cerror("Illegal #undef argument", NULLST);
- else {
- scanid(c); /* Get name to token[] */
- if (defendel(token, TRUE) == NULL) {
- cwarn("Symbol \"%s\" not defined in #undef", token);
- }
- }
- }
-
- textput(text)
- char *text;
- /*
- * Put the string in the parm[] buffer.
- */
- {
- register int size;
-
- size = strlen(text) + 1;
- if ((parmp + size) >= &parm[NPARMWORK])
- cfatal("Macro work area overflow", NULLST);
- else {
- strcpy(parmp, text);
- parmp += size;
- }
- }
-
- charput(c)
- register int c;
- /*
- * Put the byte in the parm[] buffer.
- */
- {
- if (parmp >= &parm[NPARMWORK])
- cfatal("Macro work area overflow", NULLST);
- else {
- *parmp++ = c;
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- /*
- * M a c r o E x p a n s i o n
- */
-
- static DEFBUF *macro; /* Catches start of infinite macro */
-
- expand(tokenp)
- register DEFBUF *tokenp;
- /*
- * Expand a macro. Called from the cpp mainline routine (via subroutine
- * macroid()) when a token is found in the symbol table. It calls
- * expcollect() to parse actual parameters, checking for the correct number.
- * It then creates a "file" containing a single line containing the
- * macro with actual parameters inserted appropriately. This is
- * "pushed back" onto the input stream. (When the get() routine runs
- * off the end of the macro line, it will dismiss the macro itself.)
- */
- {
- register int c;
- register FILEINFO *file;
- extern FILEINFO *getfile();
-
- #if DEBUG
- if (debug) {
- dumpadef("expand entry", tokenp);
- dumpunget("expand entry");
- }
- #endif
- /*
- * If no macro is pending, save the name of this macro
- * for an eventual error message.
- */
- if (recursion++ == 0)
- macro = tokenp;
- else if (recursion == RECURSION_LIMIT) {
- cerror("Recursive macro definition of \"%s\"", tokenp->name);
- fprintf(stderr, "(Defined by \"%s\")\n", macro->name);
- if (rec_recover) {
- do {
- c = get();
- } while (infile != NULL && infile->fp == NULL);
- unget();
- recursion = 0;
- return;
- }
- }
- /*
- * Here's a macro to expand.
- */
- nargs = 0; /* Formals counter */
- parmp = parm; /* Setup parm buffer */
- switch (tokenp->nargs) {
- case (-2): /* __LINE__ */
- sprintf(work, "%d", line);
- ungetstring(work);
- break;
-
- case (-3): /* __FILE__ */
- for (file = infile; file != NULL; file = file->parent) {
- if (file->fp != NULL) {
- sprintf(work, "\"%s\"", (file->progname != NULL)
- ? file->progname : file->filename);
- ungetstring(work);
- break;
- }
- }
- break;
-
- default:
- /*
- * Nothing funny about this macro.
- */
- if (tokenp->nargs < 0)
- cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name);
- while ((c = skipws()) == '\n') /* Look for (, skipping */
- wrongline = TRUE; /* spaces and newlines */
- if (c != '(') {
- /*
- * If the programmer writes
- * #define foo() ...
- * ...
- * foo [no ()]
- * just write foo to the output stream.
- */
- unget();
- cwarn("Macro \"%s\" needs arguments", tokenp->name);
- fputs(tokenp->name, stdout);
- return;
- }
- else if (expcollect()) { /* Collect arguments */
- if (tokenp->nargs != nargs) { /* Should be an error? */
- cwarn("Wrong number of macro arguments for \"%s\"",
- tokenp->name);
- }
- #if DEBUG
- if (debug)
- dumpparm("expand");
- #endif
- } /* Collect arguments */
- case DEF_NOARGS: /* No parameters just stuffs */
- expstuff(tokenp); /* Do actual parameters */
- } /* nargs switch */
- #if DEBUG
- if (debug)
- dumpunget("expand exit");
- #endif
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
- FILE_LOCAL int
- expcollect()
- /*
- * Collect the actual parameters for this macro. TRUE if ok.
- */
- {
- register int c;
- register int paren; /* For embedded ()'s */
- extern int charput();
-
- for (;;) {
- paren = 0; /* Collect next arg. */
- while ((c = skipws()) == '\n') /* Skip over whitespace */
- wrongline = TRUE; /* and newlines. */
- if (c == ')') { /* At end of all args? */
- /*
- * Note that there is a guard byte in parm[]
- * so we don't have to check for overflow here.
- */
- *parmp = EOS; /* Make sure terminated */
- break; /* Exit collection loop */
- }
- else if (nargs >= NMACPARS) /* Should be an error */
- cfatal("Too many actual parameters in macro expansion", NULLST); parlist[nargs++] = parmp; /* At start of new arg */
- for (;; c = cget()) { /* Collect arg's bytes */
- if (c == EOF_CHAR) {
- cerror("end of file within macro argument", NULLST);
- return (FALSE); /* Sorry. */
- }
- else if (c == '\\') { /* Quote next character */
- charput(c); /* Save the \ for later */
- charput(cget()); /* Save the next char. */
- continue; /* And go get another */
- }
- else if (type[c] == QUO) { /* Start of string? */
- scanstring(c, charput); /* Scan it off */
- continue; /* Go get next char */
- }
- else if (c == '(') /* Worry about balance */
- paren++; /* To know about commas */
- else if (c == ')') { /* Other side too */
- if (paren == 0) { /* At the end? */
- unget(); /* Look at it later */
- break; /* Exit arg getter. */
- }
- paren--; /* More to come. */
- }
- else if (c == ',' && paren == 0) /* Comma delimits args */
- break;
- else if (c == '\n') /* Newline inside arg? */
- wrongline = TRUE; /* We'll need a #line */
- charput(c); /* Store this one */
- } /* Collect an argument */
- charput(EOS); /* Terminate argument */
- #if DEBUG
- if (debug)
- printf("parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]);
- #endif
- } /* Collect all args. */
- return (TRUE); /* Normal return */
- }
-
-
-
-
-
-
-
-
-
-
- FILE_LOCAL
- expstuff(tokenp)
- DEFBUF *tokenp; /* Current macro being expanded */
- /*
- * Stuff the macro body, replacing formal parameters by actual parameters.
- */
- {
- register int c; /* Current character */
- register char *inp; /* -> repl string */
- register char *defp; /* -> macro output buff */
- int size; /* Actual parm. size */
- char *defend; /* -> output buff end */
- #if 0 && STRING_FORMAL
- int string_magic; /* String formal hack */
- #endif
- FILEINFO *file; /* Funny #include */
- extern FILEINFO *getfile();
-
- file = getfile(NBUFF, tokenp->name);
- inp = tokenp->repl; /* -> macro replacement */
- defp = file->buffer; /* -> output buffer */
- defend = defp + (NBUFF - 1); /* Note its end */
- if (inp != NULL) {
- while ((c = (*inp++ & 0xFF)) != EOS) {
- if (c == MAC_PARM) {
- c = (*inp++ & 0xFF) - 1; /* Parm number */
- #if 0 && STRING_FORMAL
- string_magic = ((c >= NMACPARS) != 0);
- if (string_magic)
- c -= NMACPARS;
- #endif
- /*
- * Replace formal parameter by actual parameter string.
- */
- if (c < nargs) {
- size = strlen(parlist[c]);
- if ((defp + size) >= defend)
- goto nospace;
- #if 0 && STRING_FORMAL
- /*
- * Erase the extra set of quotes.
- */
- if (string_magic && defp[-1] == parlist[c][0]) {
- strcpy(defp-1, parlist[c]);
- defp += (size - 2);
- }
- else {
- strcpy(defp, parlist[c]);
- defp += size;
- }
- #else
- strcpy(defp, parlist[c]);
- defp += size;
- #endif
- }
- }
- else if (defp >= defend) {
- nospace: cfatal("Out of space in macro \"%s\" arg expansion",
- tokenp->name);
- }
- else {
- *defp++ = c;
- }
- }
- }
- *defp = EOS;
- #if DEBUG
- if (debug > 1)
- printf("macroline: \"%s\"\n", file->buffer);
- #endif
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- #if DEBUG
- dumpparm(why)
- char *why;
- /*
- * Dump parameter list.
- */
- {
- register int i;
-
- printf("dump of %d parameters (%d bytes total) %s\n",
- nargs, parmp - parm, why);
- for (i = 0; i < nargs; i++) {
- printf("parm[%d] (%d) = \"%s\"\n",
- i + 1, strlen(parlist[i]), parlist[i]);
- }
- }
- #endif
-
-